home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 5 / Skunkware 5.iso / src / Games / cbzone / c_main.c < prev    next >
C/C++ Source or Header  |  1995-05-03  |  25KB  |  771 lines

  1. #include "c_includes.h"
  2. /*
  3.  * Cbzone - xbzone w/ improvements in C
  4.  *
  5.  * Version 1.0 in Fortran by Justin S. Revenaugh -- MIT (5/86)
  6.  * C port and modifications by Todd W. Mummert -- CMU (12/90)
  7.  *   email bugs and comments concerning this game to:
  8.  *                 mummert+@sam.cs.cmu.edu
  9.  *
  10.  * Copyright Notice:  This program is freely distributable on a
  11.  * nonprofit basis as long as this notice is maintained on all
  12.  * copies. Inclusion of any program code or derivative thereof
  13.  * for commercial purposes is expressly forbidden. 
  14.  * December 10, 1990.
  15.  *
  16.  * RCS Info
  17.  *  $Header: c_main.c,v 1.1 93/02/25 13:46:54 iv Locked $
  18.  *
  19.  * Bugs/Features:
  20.  *   Tanks still pass through each other.  Missiles/copters don't
  21.  *   collide with one another unless below 80 ft.  Consider these
  22.  *   features for now.
  23.  *
  24.  * Enhancement Ideas:
  25.  *  -Convert the graphics routines to general purpose routines...
  26.  *   The graphics are either multiline(segments) or polyline(connected).
  27.  *   Polyline lines can either be open or closed.  This could all be
  28.  *   done in a single routine with the number of mlines and plines
  29.  *   passed in...maybe too much bookkeeping involved.
  30.  *  -Have the ability to have both tanks and missiles on the screen at
  31.  *   the same time.  Never bothered to do it, for I initially thought
  32.  *   it would be unplayable.  Not so sure now.
  33.  *  -Make this multiplayer...two basic approaches.  The first is one
  34.  *   program controlling two displays.  The second is running on
  35.  *   different machines, passing the necessary objects back/forth.  Since
  36.  *   you wouldn't need to pass graphics information, this is not
  37.  *   a lot of information to update. Instead of a call to move<enemy>
  38.  *   you would get back the changes made by your opponent.
  39.  *
  40.  *  -If you make any interesting changes, please send them to me and I'll
  41.  *   incorporate them into the next release.
  42.  */
  43.  
  44. Option option;
  45. Optionp opt = &option;
  46.  
  47. /*
  48.  * Just initialize the array prior to starting play.  Associate
  49.  * the salvos with the correct objects.  Set the cosines of some
  50.  * of the angles to 1.
  51.  */
  52. void initarray(o)
  53.      Genericp o;
  54. {
  55.   Genericp s;
  56.   Genericp g;
  57.   Genericp pl = o;
  58.   int i, j;
  59.   
  60.   pl->type = IS_PLAYER;                 /* player is always the first */
  61.   pl->attr = START_LIVING;              /* object.  Same size as the */
  62.   pl->criticalx = 45.0;                 /* other tanks.  */
  63.   pl->criticaly = 70.0;
  64.   for (g=o; g<o+opt->mobjects; g++) {
  65.     g->ca = 1.0;                        /* for those objects which */
  66.     for (j=0; j<5; j++)                 /* have rotating parts, set */
  67.       g->dc[j].ctp = g->dc[j].cta = 1.0;  /* the cos to 1.0 */
  68.   }
  69.  
  70.   s = o+opt->sstart;                    /* player gets msalvos, each  */
  71.   pl->salvo = s;                        /* enemy gets one.  the salvos */
  72.   for (i=0; i<opt->msalvos; i++) {      /* must know who their owner */
  73.     s->salvo = pl;                      /* is for a variety of reasons */
  74.     s->type = IS_SALVO;                 /* first the player */
  75.     s->lntype = LN_SALVO;
  76.     s++;
  77.   }
  78.   for (g=o+opt->estart; g<o+opt->lstart; g++) {
  79.     g->salvo = s;                       /* now the enemies */
  80.     s->salvo = g;
  81.     s->type = IS_SALVO;
  82.     s->lntype = LN_SALVO;
  83.     s++;
  84.   }
  85. }
  86.  
  87. /*
  88.  * The main routine for cbzone.  Probably more complicated than it
  89.  * need be...but it takes care of all the interobject dependencies.
  90.  * Therefore other routines work on a single object (in general).
  91.  * placeobjects() will place however many objects it can, while
  92.  * scanner() needs to know where all the enemies are.
  93.  */
  94. void main(argc, argv)
  95.      int argc;
  96.      char* argv[];
  97. {
  98.   Genericp o;
  99.   Genericp pl, g, g2, s;
  100.   char key;
  101.   float alpha, ddx2, ddx, ddy2, ddy, dif, diff, dx, dy, v, dist;
  102.   float blocksize = 100.0;
  103.   float blocksizesqrd = 10000.0;
  104.   float check, testx, testy;
  105.   float landerthreshold = 0.5;
  106.   float threshold = 0.7;
  107.   int deadcount, i, icheck, position[2];
  108.   int nummissile = 0;
  109.   int missilecount = 0;
  110.   int nextmissile = 1200;
  111.   int numleft = 3;
  112.   int score = 0;
  113.   int scorebase = 0;
  114.   Bool new_salvo_flag, new_sight_flag, event, tank_stranded;
  115.   Bool aligned = False;
  116.   Bool blocked_flag = False;
  117.   Bool dead = False;
  118.   Bool first = True;
  119.   Bool firstmissile = True;
  120.   Bool keylast = True;
  121.   Bool lander = False;
  122.   Bool missilerun = False;
  123.   Bool salvo_flag = False;
  124.   Bool sens = False;
  125.   Bool sight_flag = False;
  126.   extern long time();
  127.   struct timeval tstart;
  128.   struct timeval tend;
  129.   long tdiff, limit;
  130. #ifdef DEVELOPER
  131.   int passes = 0;
  132.   struct timeval game_start;
  133.   struct timeval game_end;
  134. #endif DEVELOPER
  135.   
  136.   gprinqconfig(&argc, argv);
  137.   limit = opt->delay * 1.2e4;
  138.  
  139.   /* now that we have parsed the options, we know how large to */
  140.   /* make the world.  Use calloc here as most of the array     */
  141.   /* should start off 0.                                       */
  142.      
  143.   pl = o = (Genericp) calloc(opt->mobjects,sizeof(Generic));
  144.  
  145.   if (o == NULL) {
  146.     printf("Malloc failed...trying to create too many objects?\n");
  147.     exit(1);
  148.   }
  149.   
  150.   initarray(o);                         /* prepare the main array */
  151.   srand48(time((long *) 0));            /* start things off randomly */
  152.   screeninit();
  153.   updatedisplay(missilerun, lander, score, numleft, sens, False);
  154.   xhairs(aligned);
  155.   gprinqcursor(position);
  156.   event = gprcondeventwait(&key, position);
  157.   joystick(position, sens, pl);
  158.  
  159.   /* place the objects out there to start the game.  if the player is */
  160.   /* is_new, then the objects are placed at a random distance, else   */
  161.   /* the objects get placed on the horizon.                           */
  162.  
  163.   placeobjects(o, missilerun, score);
  164.   pl->attr &= ~IS_NEW;                  /* now the objects can be */
  165.                                         /* placed at the horizon. */
  166.  
  167.   /* now calculate ranges to all the objects and translate them */
  168.   /* into a player-centric coordinate system                    */
  169.  
  170.   pl->ca = cos(pl->azm);
  171.   pl->sa = sin(pl->azm);
  172.   for (g=o+opt->estart; g<o+opt->mobjects; g++)
  173.     if (g->attr & IS_ALIVE) {
  174.       dx = g->x - pl->x;
  175.       dy = g->y - pl->y;
  176.       g->range = sqrt(dx*dx + dy*dy);
  177.       g->proy = -dx * pl->sa + dy * pl->ca;
  178.       g->prox = dx * pl->ca + dy * pl->sa;
  179.     }
  180.  
  181.   scanner(o);
  182.   drawhorizon(pl->azm); 
  183.  
  184.   /* now the work really starts....we just iterate through */
  185.   /* the following loop until the player dies or quits     */
  186. #ifdef DEVELOPER
  187.   gettimeofday(&game_start, 0);
  188. #endif
  189.   while (1) {
  190.     gettimeofday(&tstart, 0);
  191.     gprinqcursor(position);
  192.     event = gprcondeventwait(&key, position);
  193.  
  194.     if (event && key == 'Q') {
  195.       free(o);
  196.       exit(scores(score));
  197.     }
  198.  
  199.     if (event && key == 'R') {
  200.       clearentirescreen();
  201.       staticscreen();
  202.       updatedisplay(False, False, -1, 0, False, True);
  203.       updatedisplay(missilerun, lander, score, numleft, sens, False);
  204.       if (sight_flag)
  205.         message(1, False);
  206.       if (pl->attr & IS_BLOCKED)
  207.         message(2, False);
  208.       if (salvo_flag)
  209.         message(3, False);
  210.       scanner(o);
  211.       xhairs(aligned);
  212.       drawhorizon(pl->azm); 
  213.     }
  214.  
  215.     joystick(position, sens, pl);
  216.     if (paused)
  217.       continue;
  218.     
  219.     for (i=0; i<opt->msalvos; i++) {    /* now find a shot we can use */
  220.       s = pl->salvo+i;
  221.       if (!(s->attr & STILL_THERE))
  222.         break;
  223.       s = NULL;
  224.     }
  225.     if (event && pl->attr & IS_ALIVE)
  226.       if (keylast) {
  227.         for (i=0; i<opt->msalvos; i++) { /* now find a shot we can use */
  228.           s = pl->salvo+i;
  229.           if (!(s->attr & STILL_THERE))
  230.             break;
  231.           s = NULL;
  232.         }
  233.         if (key == 'a' && s!=NULL) {    /* fire up one shot */
  234.           s->attr = START_LIVING;
  235.           s->ecount = 0;
  236.           s->x = pl->x;
  237.           s->y = pl->y;
  238.           s->z = 0.0;
  239.           s->prox = 0;
  240.           s->proy = 0;
  241.           s->azm = pl->azm;
  242.           s->speed = 40.0;
  243.           keylast = False;
  244.         }
  245.         else if (key == 'b') {          /* center our joystick */
  246.           position[0] = 500;
  247.           position[1] = 355;
  248.           gprsetcursorposition(position);
  249.           joystick(position, sens, pl);
  250.           keylast = False;
  251.         }
  252.         else if (key == 'c') {          /* toggle sensitivity */
  253.           sens = !sens;
  254.           joystick(position, sens, pl);
  255.           keylast = False;
  256.         }
  257.       }
  258.       else if (key == 'A' || key == 'B' || key == 'C')
  259.         keylast = True;                 /* button released */
  260.   
  261.     /* if we can move, update our rotation, bearing (azimuth), and */
  262.     /* position.                                                   */
  263.     
  264.     if (pl->attr & IS_ALIVE && !(pl->attr & IS_BLOCKED)) {
  265.       pl->azm += pl->rotate;
  266.       if (pl->azm > PI2)
  267.         pl->azm -= PI2;
  268.       if (pl->azm <= 0.0)
  269.         pl->azm += PI2;
  270.       pl->ca = cos(pl->azm);
  271.       pl->sa = sin(pl->azm);
  272.     }
  273.     if (pl->attr & IS_ALIVE) {
  274.       pl->x -=  pl->sa * pl->speed;
  275.       pl->y +=  pl->ca * pl->speed;
  276.     }
  277.     else
  278.       pl->speed = 0.0;
  279.  
  280.     /* now call the move generation routines for the objects */
  281.     /* which require thought,  speed and/or rotation may be  */
  282.     /* affected.                                             */
  283.     
  284.     for (g=o+opt->estart; g<o+opt->sstart; g++)
  285.       if (g->attr & IS_ALIVE)
  286.         switch (g->type) {
  287.         case IS_TANK:
  288.           movetank(g, pl); break;
  289.         case IS_SUPER:
  290.           movesuper(g, pl); break;
  291.         case IS_MISSILE:
  292.           movemissile(g, pl, first); break;
  293.         case IS_COPTER:
  294.           movecopter(g, pl); break;
  295.         case IS_LANDER:
  296.           movelander(g, pl); break;
  297.         default:
  298.           printf("Help! Something's alive and I don't know what...\n");
  299.           exit(1);
  300.         }
  301.  
  302.     /* now update their bearing and position */
  303.     
  304.     for (g=o+opt->estart; g<o+opt->lstart; g++) {
  305.       if (g->attr & IS_ALIVE && !(g->attr & IS_BLOCKED)) 
  306.         g->azm += g->rotate;
  307.       g->ca = cos(g->azm);
  308.       g->sa = sin(g->azm);
  309.       g->x -=  g->sa * g->speed;
  310.       g->y +=  g->ca * g->speed;
  311.     }
  312.     for (g=o+opt->lstart; g<o+opt->bstart; g++)
  313.       if (g->attr & IS_ALIVE) {
  314.         g->ca = cos(g->azm);
  315.         g->sa = sin(g->azm);
  316.         g->x -= g->sa * g->speed;
  317.         g->y += g->ca * g->speed;
  318.       }
  319.  
  320.     /* now compute ranges from objects to the player */
  321.     
  322.     for (g=o+opt->estart; g<o+opt->mobjects; g++) 
  323.       if (g->attr & STILL_THERE)
  324.         g->range = sqrt(DIST(g, pl));
  325.  
  326.     for (g=o; g<o+opt->lstart; g++)     /* assume all objects are */
  327.       g->attr &= ~IS_BLOCKED;           /* unblocked              */
  328.  
  329.     /* now check to see if they really were unblocked.  If not, then */
  330.     /* project them back along their path until they are.  This      */
  331.     /* section just checks for being blocked by blocks.              */
  332.  
  333.     for (g=o+opt->bstart; g<o+opt->mobjects; g++) {
  334.       if (g->range < blocksize) {
  335.         pl->attr |= BLOCKED_BY_BLOCK;
  336.         dx = pl->x - g->x;
  337.         dy = pl->y - g->y;
  338.         diff = dy * pl->ca - dx * pl->sa;
  339.         if (pl->speed > 0.0)
  340.           v = diff + sqrt(diff*diff + blocksizesqrd - g->range*g->range);
  341.         else if (pl->speed < 0.0) 
  342.           v = diff - sqrt(diff*diff + blocksizesqrd - g->range*g->range);
  343.         pl->x += pl->sa * v;
  344.         pl->y -= pl->ca * v;
  345.       }
  346.       for (g2=o+opt->estart; g2<o+opt->lstart; g2++)
  347.         if (g2->attr & IS_ALIVE &&
  348.             (dist = DIST(g, g2)) < blocksizesqrd) {
  349.           g2->attr |= BLOCKED_BY_BLOCK;
  350.           if (!(g2->type & (IS_MISSILE | IS_COPTER))) {
  351.             dx = g2->x - g->x;
  352.             dy = g2->y - g->y;
  353.             diff = dy * g2->ca - dx * g2->sa;
  354.             if (g2->speed > 0.0) 
  355.               v = diff + sqrt(diff*diff + blocksizesqrd - dist);
  356.             else if (g2->speed < 0.0) 
  357.               v = diff - sqrt(diff*diff + blocksizesqrd - dist);
  358.             g2->x += g2->sa * v;
  359.             g2->y -= g2->ca * v;
  360.           }
  361.         }
  362.     }
  363.  
  364.     /* if the player moved, or if an enemy did, we need to recompute */
  365.     /* the range to that enemy.                                      */
  366.     
  367.     for (g=o+opt->estart; g<o+opt->lstart; g++)
  368.       if (g->attr & IS_ALIVE &&
  369.           (g->attr & IS_BLOCKED || pl->attr & IS_BLOCKED))
  370.         g->range = sqrt(DIST(g, pl));
  371.  
  372.     /* now check to see if the player is blocked by any enemy.  */
  373.     /* if so, project them back.                                */
  374.  
  375.     for (g=o+opt->estart; g<o+opt->lstart; g++) 
  376.       if (g->attr & IS_ALIVE && g->range < blocksize)
  377.         if (!(g->type & (IS_MISSILE | IS_COPTER))) {
  378.           if (g->attr & IS_BLOCKED) {
  379.             pl->speed = 0.0;
  380.             if (fabs(g->speed) < 0.001)
  381.               g->speed = sign(0.001, g->speed);
  382.           }
  383.           pl->attr |= BLOCKED_BY_ENEMY;
  384.           g->attr |= BLOCKED_BY_ENEMY;
  385.           ddx = pl->speed * pl->sa - g->speed * g->sa;
  386.           ddy = pl->speed * pl->ca - g->speed * g->ca;
  387.           ddx2 = ddx*ddx;
  388.           ddy2 = ddy*ddy;
  389.           dx = pl->x - g->x;
  390.           dy = pl->y - g->y;
  391.           dif = ddy * dy - ddx * dx;
  392.           alpha = (dif + sqrt(dif*dif + (blocksizesqrd - g->range*g->range)
  393.                               * (ddx2 + ddy2))) / (ddx2 + ddy2);
  394.           pl->x += alpha * pl->speed * pl->sa;
  395.           pl->y -= alpha * pl->speed * pl->ca;
  396.           g->x += alpha * g->speed * g->sa;
  397.           g->y -= alpha * g->speed * g->ca;
  398.         }
  399.  
  400.     /* if we've moved, recompute distance to all the salvos */
  401.     
  402.     if (pl->attr & IS_BLOCKED)
  403.       for (g=o+opt->sstart; g<o+opt->bstart; g++)
  404.         if (g->attr & IS_ALIVE)
  405.           g->range = sqrt(DIST(g, pl));
  406.  
  407.     /* enemies disappear if their range is greater than 2200. */
  408.     /* We check last[0] to see if they need to be erased.  In */
  409.     /* most cases probably not, unless we really screw with   */
  410.     /* their speed.                                           */
  411.  
  412.     tank_stranded = False;
  413.     for (g=o+opt->estart; g<o+opt->lstart; g++) {
  414.       g->ecount++;
  415.       if (g->attr & IS_ALIVE)
  416.         if (g->range > 2200.0) 
  417.           if (g->dc[0].last)
  418.             g->attr = ERASE;
  419.           else
  420.             g->attr = 0;
  421.         else if (g->type & (IS_SUPER | IS_TANK) &&
  422.                  g->ecount > TANK_STRAND_COUNT)
  423.           tank_stranded = True;
  424.     }
  425.  
  426.     /* landers are out of range at 2750 */
  427.     
  428.     for (g=o+opt->lstart; g<o+opt->sstart; g++) {
  429.       g->ecount++;
  430.       if (g->attr & IS_ALIVE && g->range > 2750.0)
  431.         if (g->dc[0].last)
  432.           g->attr = ERASE;
  433.         else
  434.           g->attr = 0;
  435.     }
  436.  
  437.     /* blocks also at 2200 */
  438.     
  439.     for (g=o+opt->bstart; g<o+opt->mobjects; g++)
  440.       if (g->range > 2200.0)
  441.         if (g->dc[0].last)
  442.           g->attr = ERASE;
  443.         else
  444.           g->attr = 0;
  445.  
  446.     /* salvos are never out of range, but their lifetime is limited */
  447.     
  448.     for (g=o+opt->sstart; g<o+opt->bstart; g++) {
  449.       g->ecount++;
  450.       if (g->attr & IS_ALIVE && g->ecount > 50)
  451.         if (g->dc[0].last)
  452.           g->attr = ERASE;
  453.         else
  454.           g->attr = 0;
  455.     }
  456.  
  457.     /* we never set the 'salvo fired' message in this routine.  */
  458.     /* however, we do have to turn it off.  if salvos are alive */
  459.     /* we assume the message is on, once no enemy salvos are    */
  460.     /* alive we turn it off.                                    */
  461.     
  462.     new_salvo_flag = False;
  463.     for (g=o+opt->sstart; g<o+opt->bstart; g++) 
  464.       if (g->attr & IS_ALIVE) {         /* if salvo exist and   */
  465.         if (g->salvo != pl)             /* not owned by player  */
  466.           new_salvo_flag = True;        /* then the flag is set */
  467.         
  468.         /* check to see if a salvo hits a block */
  469.  
  470.         for (g2=o+opt->bstart; g2<o+opt->mobjects; g2++)
  471.           if (fabs(g2->x - g->x) < g2->criticalx &&
  472.               fabs(g2->y - g->y) < g2->criticaly) {
  473.             g->attr = START_EXPLODING;
  474.             g->ecount = 0;
  475.           }
  476.         
  477.         /* now check to see if the salvo kills a lander. */
  478.         /* If so, and the player fired the salvo, update */
  479.         /* the score.                                    */
  480.         
  481.         for (g2=o+opt->lstart; g2<o+opt->sstart; g2++)
  482.           if (g2->attr & IS_ALIVE)
  483.             if (DIST(g, g2) < g2->criticalx) {
  484.               g->attr = START_EXPLODING;
  485.               g2->attr = START_EXPLODING;
  486.               g->ecount = 0;
  487.               g2->ecount = 0;
  488.               if (g->salvo == pl) {
  489.                 score += 10000;
  490.                 icheck = score / 100000;
  491.                 if (icheck > scorebase) {
  492.                   numleft++;
  493.                   if (numleft > 4)
  494.                     numleft = 4;
  495.                   scorebase = icheck;
  496.                 }
  497.               }
  498.             }
  499.  
  500.         /* now check to see if the salvo hit any enemy.   The salvo */
  501.         /* cannot hit the one who fired it.  This prevents range    */
  502.         /* checking problems when first fired.                      */
  503.         
  504.         for (g2=o+opt->estart; g2<o+opt->lstart; g2++)
  505.           if (g2->attr & IS_ALIVE && g->salvo != g2) {
  506.             dx = g->x - g2->x;
  507.             dy = g->y - g2->y;
  508.             testx = fabs( dx * g2->ca + dy * g2->sa);
  509.             testy = fabs(-dx * g2->sa + dy * g2->ca);
  510.             if (testx < g2->criticalx && testy < g2->criticaly &&
  511.                 (!(g2->type & (IS_MISSILE | IS_COPTER)) || g2->z < 80.0)) {
  512.               g->attr = START_EXPLODING;
  513.               g2->attr = START_EXPLODING;
  514.               g->ecount = 0;
  515.               g2->ecount = 0;
  516.  
  517.               /* if the player fired, give him credit */
  518.               
  519.               if (g->salvo == pl) {
  520.                 if (g2->type & IS_SUPER) 
  521.                   score += 5000;
  522.                 else if (g2->type & (IS_MISSILE | IS_COPTER)) {
  523.                   score += 5000;
  524.                   nummissile--;
  525.                 }
  526.                 else
  527.                   score += 3000;
  528.                 icheck = score / 100000;
  529.                 if (icheck > scorebase) {
  530.                   numleft++;
  531.                   if (numleft > 4)
  532.                     numleft = 4;
  533.                   scorebase = icheck;
  534.                 }
  535.               }
  536.             }
  537.           }
  538.       }
  539.  
  540.     /* check to see if a missile or copter rams a lander */
  541.  
  542.     for (g=o+opt->estart; g<o+opt->lstart; g++)
  543.       if (g->type & (IS_MISSILE | IS_COPTER) && g->attr & IS_ALIVE)
  544.         for (g2=o+opt->lstart; g2<o+opt->sstart; g2++)
  545.           if (g2->attr & IS_ALIVE &&
  546.               DIST(g, g2) < g2->criticalx && g->z < 80) {
  547.             nummissile--;
  548.             g->attr = START_EXPLODING;
  549.             g2->attr = START_EXPLODING;
  550.             g->ecount = 0;
  551.             g2->ecount = 0;
  552.           }
  553.     
  554.     /* now check if we need to draw the object.  */
  555.     /* convert into player-centric coordinates   */
  556.     /* and project a cone forward to see if the  */
  557.     /* enemy is within it. Also align the gun    */
  558.     /* gun sights if necessary.                  */
  559.  
  560.     new_sight_flag = False;
  561.     aligned = False;
  562.     lander = False;
  563.     for (g=o+opt->estart; g<o+opt->mobjects; g++) 
  564.       if (g->attr & (IS_ALIVE|IS_EXPLODING)) {
  565.         g->dc[0].seen = False;
  566.         if (g->range < 2000.0) {
  567.           dx = g->x - pl->x;
  568.           dy = g->y - pl->y;
  569.           g->proy = -dx * pl->sa + dy * pl->ca;
  570.           g->prox =  dx * pl->ca + dy * pl->sa;
  571.           check = g->proy / (fabs(g->prox) + 1.0);
  572.           if (check > threshold) {
  573.             g->dc[0].seen = True;
  574.             if (g->type & IS_ENEMY && g->attr & IS_ALIVE)
  575.               new_sight_flag = True;
  576.             if (fabs(g->prox) < 50 && g->attr & IS_ALIVE &&
  577.                 !(g->type & (IS_ABLOCK | IS_SALVO)))
  578.               aligned = True;
  579.           }
  580.           if (g->type & IS_LANDER && g->attr & IS_ALIVE
  581.               && check > landerthreshold) {
  582.             lander = True;
  583.             if (fabs(g->prox) < 60)
  584.               aligned = True;
  585.           }
  586.         }
  587.       }
  588.     
  589.     /* change the various messages, if necessary.  Never use */
  590.     /* the bell, unless opt->loud is True.                   */
  591.     
  592.     if (sight_flag && !new_sight_flag) {
  593.       message(-1, False);
  594.       sight_flag = False;
  595.     }
  596.     else if (!sight_flag && new_sight_flag) {
  597.       message(1, False);
  598.       sight_flag = True;
  599.     }
  600.     if (pl->attr & IS_BLOCKED && !blocked_flag) {
  601.       message(2, True);
  602.       blocked_flag = True;
  603.     }
  604.     else if (!(pl->attr & IS_BLOCKED) && blocked_flag) {
  605.       message(-2, False);
  606.       blocked_flag = False;
  607.     }
  608.     if (salvo_flag && !new_salvo_flag) {
  609.       message(-3, False);
  610.       salvo_flag = False;
  611.     }
  612.     else if (!salvo_flag && new_salvo_flag)
  613.       salvo_flag = True;
  614.     
  615.     scanner(o);
  616.     updatedisplay(missilerun, lander, score, numleft, sens, False);
  617.     xhairs(aligned);
  618.     drawhorizon(pl->azm); 
  619.  
  620.     /* now draw all the objects */
  621.     
  622.     for (g=o+opt->estart; g<o+opt->mobjects; g++) {
  623.       if (g->attr & (IS_ALIVE | ERASE)) 
  624.         drawobject(g, pl);
  625.       else if (g->attr & (IS_EXPLODING | EXERASE))
  626.         switch (g->type) {
  627.         case IS_SALVO:
  628.           explodesalvo(g, pl); break;
  629.         case IS_COPTER:
  630.         case IS_MISSILE:
  631.         case IS_LANDER:
  632.         case IS_TANK:
  633.         case IS_SUPER:
  634.           explodeobject(g, pl); break;
  635.         default:
  636.           printf("Help! Cannot explode what doesn't exist.\n");
  637.           exit(1);
  638.         }
  639.       g->attr &= ~(ERASE | EXERASE);
  640.     }
  641.  
  642.     /* now start checking for player death.  if there is a missile, */
  643.     /* check to see if it rammed the player.                        */
  644.     
  645.     if (missilerun)
  646.       for (g=o+opt->estart; g<o+opt->lstart; g++)
  647.         if (g->attr & IS_ALIVE && g->type & (IS_MISSILE | IS_COPTER) && 
  648.             g->range < blocksize && g->z < 80) {
  649.           g->attr = START_EXPLODING;
  650.           drawcracks();
  651.           pl->attr &= ~IS_ALIVE;
  652.           dead = True;
  653.           deadcount = 0;
  654.         }
  655.  
  656.     /* check to see if any salvos hit. */
  657.  
  658.     for (g=o+opt->sstart; g<o+opt->bstart; g++)
  659.       if (g->attr & IS_ALIVE && g->salvo != pl &&
  660.           g->range < 100.0 && fabs(g->prox) < pl->criticalx &&
  661.           fabs(g->proy) < pl->criticaly) {
  662.         drawobject(g, pl);
  663.         g->attr = 0;
  664.         drawcracks();
  665.         pl->attr &= ~IS_ALIVE;
  666.         dead = True;
  667.         deadcount = 0;
  668.       }
  669.  
  670.     /* if we are dead, redraw the cracks every five turns.  after 50 */
  671.     /* turns, we can start playing again if we have any lives left.  */
  672.     
  673.     if (dead) {
  674.       if (deadcount%5 == 0)
  675.         drawcracks();
  676.       if (deadcount > 50) {
  677.         dead = False;
  678.         numleft--;
  679.         if (numleft < 0) {
  680. #ifdef DEVELOPER
  681.           gettimeofday(&game_end, 0);
  682.           if (opt->output)
  683.             printf("The game took an average %10.8f secs.\n",
  684.                    (game_end.tv_sec-game_start.tv_sec +
  685.                     (game_end.tv_usec-game_start.tv_usec)*1.0e-6)/passes);
  686. #endif DEVELOPER
  687.           free(o);
  688.           exit(scores(score));
  689.         }
  690.         if (missilerun) {
  691.           nummissile -= 2;
  692.           if (nummissile <= 0 && opt->mtanks) {
  693.             missilerun = False;
  694.             nextmissile = 750 * frand() + 750;
  695.           }
  696.         }
  697.         clearscreen();
  698.         pl->x = 0.0;                    /* reset all our attributes */
  699.         pl->y = 0.0;
  700.         pl->speed = 0.0;
  701.         pl->azm = 0.0;
  702.         pl->ca = 1.0;
  703.         pl->sa = 0.0;
  704.         pl->attr = START_LIVING;
  705.         message(-1, False);             /* turn off all the messages */
  706.         sight_flag = False;
  707.         message(-2, False);
  708.         blocked_flag = False;
  709.         message(-3, False);
  710.         salvo_flag = False;
  711.         for (g=o+opt->estart; g<o+opt->mobjects; g++) 
  712.           g->attr = 0;                  /* remove all objects */
  713.       }
  714.     }
  715.  
  716.     /* Now schedule the missile runs.  There will be a missile run if */
  717.     /* mtanks==0 or we are in copter practice or if a tank has been   */
  718.     /* around too long or we haven't had a missile in a while.        */
  719.  
  720.     if (pl->attr & IS_ALIVE && opt->mmissiles &&
  721.         (!opt->mtanks || opt->copters ||
  722.          tank_stranded || missilecount > nextmissile))
  723.       if (!missilerun) {
  724.         missilecount = 0;
  725.         for (g=o+opt->estart; g<o+opt->bstart; g++)
  726.           if (!(g->type & (IS_LANDER | IS_MISSILE | IS_COPTER)))
  727.             if (g->attr & IS_ALIVE)
  728.               g->attr = ERASE;
  729.             else if (g->attr & IS_EXPLODING)
  730.               g->attr = EXERASE;
  731.         nummissile = frand() * 3 * opt->mmissiles + 1;
  732.         if (firstmissile)
  733.           nummissile = 1;
  734.         first = firstmissile;
  735.         firstmissile = False;
  736.         missilerun = True;
  737.       }
  738.  
  739.     /* once this run is over, schedule another one for a later date */
  740.  
  741.     if (missilerun && opt->mtanks && nummissile <= 0) {
  742.       missilerun = False;
  743.       nextmissile = 750 * frand() + 750;
  744.     }
  745.  
  746.     /* now place whatever objects need to be placed.  */
  747.  
  748.     if (!dead)
  749.       placeobjects(o, missilerun, score);
  750.     pl->attr &= ~IS_NEW;                /* in case the player was new */
  751.  
  752.     deadcount++;
  753.     if (!missilerun) missilecount++;
  754.  
  755. /* use timeclock here instead of gettimeofday to get a sync(d, 0)
  756.  * just in case there are graphics we need to draw.
  757.  */
  758.     timeclock(&tend); 
  759.     tdiff = limit -
  760.       ((tend.tv_sec-tstart.tv_sec)*1e6+tend.tv_usec-tstart.tv_usec);
  761.     if (tdiff > 0) {
  762.       tend.tv_sec = 0;
  763.       tend.tv_usec = tdiff;
  764.       select(0, 0, 0, 0, &tend);
  765.     }
  766. #ifdef DEVELOPER
  767.     passes++;
  768. #endif DEVELOPER
  769.   }
  770. }
  771.